{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "26cd3a36-cff4-4237-83b0-51a4bc1cbbc7",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "欢迎使用商品库存管理系统!\n",
      "是否进入程序？\n"
     ]
    },
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "请输入y/n:  y\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "感谢使用商品库存管理系统,请查看功能清单:\n",
      "1.维护商品清单\n",
      "2.入库管理\n",
      "3.出库管理\n",
      "4.库存总览\n",
      "5.退出功能\n"
     ]
    },
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "请输入功能序号:  1\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "欢迎使用商品清单维护功能，请选择要执行的操作: \n",
      "1.查看商品清单\n",
      "2.维护商品清单\n",
      "3.退出本菜单\n"
     ]
    },
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "请输入功能序号:  1\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "是否查看汇总信息?\n"
     ]
    },
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "请输入y/n:  y\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "生鲜食品:\n",
      "该品类下无商品\n",
      "包装食品:\n",
      "该品类下商品为:\n",
      "1.薯片\n",
      "2.紫菜\n",
      "酒水:\n",
      "该品类下无商品\n",
      "家电:\n",
      "该品类下无商品\n",
      "厨具:\n",
      "该品类下无商品\n",
      "卫生日用品:\n",
      "该品类下无商品\n",
      "目前商品清单中共有1个品类，合计2种商品\n",
      "感谢使用商品库存管理系统,请查看功能清单:\n",
      "1.维护商品清单\n",
      "2.入库管理\n",
      "3.出库管理\n",
      "4.库存总览\n",
      "5.退出功能\n"
     ]
    },
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "请输入功能序号:  5\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "谢谢使用!再见！\n"
     ]
    }
   ],
   "source": [
    "# 商品库存管理小程序\n",
    "#从实际业务出发考虑，按品类定好商品的目录\n",
    "commodities={\n",
    "    '生鲜食品':[],\n",
    "    '包装食品':[],\n",
    "    '酒水':[],\n",
    "    '家电':[],\n",
    "    '厨具':[],\n",
    "    '卫生日用品':[]\n",
    "}#key为品类，value为列表，这个列表装入所有该品类下的商品品名\n",
    "\n",
    "storage={\n",
    "    '生鲜食品':[],\n",
    "    '包装食品':[],\n",
    "    '酒水':[],\n",
    "    '家电':[],\n",
    "    '厨具':[],\n",
    "    '卫生日用品':[]\n",
    "}#key为品类，value为列表，这个列表装所有该品类下的商品信息，商品信息为字典，即：字典嵌套列表再嵌套字典\n",
    "\n",
    "in_storage={\n",
    "    '生鲜食品':[],\n",
    "    '包装食品':[],\n",
    "    '酒水':[],\n",
    "    '家电':[],\n",
    "    '厨具':[],\n",
    "    '卫生日用品':[]\n",
    "}#key为品类，value为列表，这个列表装所有该品类下的商品信息，商品信息为字典，即：字典嵌套列表再嵌套字典，\n",
    "#如果只是这个程序跑通，只取入库的品类、品名、数量、单价、总价，没必要维护入库字典，但考虑到如果添加储存功能或查看历史入库信息，那还是很有必要的\n",
    "\n",
    "out_storage={\n",
    "    '生鲜食品':[],\n",
    "    '包装食品':[],\n",
    "    '酒水':[],\n",
    "    '家电':[],\n",
    "    '厨具':[],\n",
    "    '卫生日用品':[]\n",
    "    }#key为品类，value为列表，这个列表装所有该品类下的商品信息，商品信息为字典，即：字典嵌套列表再嵌套字典，\n",
    "#跟入库字典同理，出库字典也是为了储存功能及查看历史出库信息。\n",
    "\n",
    "#将后面会大量使用的选择进行封装\n",
    "def choice_yn(prompt='请输入y/n: '):\n",
    "    while True:\n",
    "        choice=input(prompt).strip()\n",
    "        match choice:\n",
    "            case 'y'|'n':\n",
    "                return choice\n",
    "            case _:\n",
    "                continue\n",
    "#在实际编程过程中我发现，后面会大量使用到需要给某一品类下的产品进行编号并对其选择进行某些操作的情形，因此需要设计了这么一个封装\n",
    "def safe_enu(lst,empty='该品类下无商品'):\n",
    "    if len(lst)==0:\n",
    "        return empty,{}#print只显示打印，输出的值是None，没有输出任何可后续操作的内容，因此要把想输出的内容return出去，这里返回empty和空字典{}的原因是我需要在商品品名非空时返回一个包含两个元素的元组并进行解包，因此任何情形下都必须返回两个元素以便解包能对应上\n",
    "    else:\n",
    "        lines=['该品类下商品为:']#同理，如果这个函数一直只作为打印存在，可以不return，但在后续的代码中将多次使用这个模块输出的判断结果，因此必须把内容'可输出化'，或者说'结果化'\n",
    "        commos={}#设置这个空字典是因为，显示打印的排序后商品只是打印值，无法直接使用，因此需要生成一个序号，品名都跟打印值一样的东西，在用户输入数字序号后能提取到品名并供下一步操作，因此设置了这个以序号为key，品名为value的字典，同时这个字典跟打印排序在同一个遍历下，保证了它的顺序一定和打印排序的一致。\n",
    "        for i, name in enumerate(lst,start=1):\n",
    "            lines.append(f'{i}.{name}')#在列表后添加元素，每个遍历的符合要求的元素都会加入列表\n",
    "            commos[i]=name#这是一个字典，不是列表，i是key，name是value\n",
    "        return '\\n'.join(lines), commos#如上所述，解包到两个变量身上后续使用\n",
    "\n",
    "#以下这个的目的是对应不同功能菜单的可选项数量不同的需求，以及上面提到的不同的品类的面不固定的商品数量的选择\n",
    "def func_num(num_of_functions):#num_of_functions指的是可供选择的功能的个数，封装这个功能的目的是因为分支程序中有不少都要选择功能的，只是功能名称不同而已，但都是选择数字序号而已\n",
    "    while True:\n",
    "        picknum=input('请输入功能序号: ').strip()\n",
    "        if picknum.isdigit():\n",
    "            picknum=int(picknum)\n",
    "            if 0<int(picknum)<=num_of_functions:\n",
    "                return picknum\n",
    "            else:\n",
    "                print('非法输入!请输入功能序号范围内的数字')\n",
    "        else:\n",
    "            print('非法输入!请输入功能序号范围内的数字')\n",
    "            continue\n",
    "\n",
    "#这个封装的目的是为了让用户在数个情境下正确输入非空的品名。\n",
    "def inputname():\n",
    "    while True:\n",
    "        name=input('请输入商品品名: ').strip()\n",
    "        match name:\n",
    "            case c if c:\n",
    "                return c\n",
    "            case _:\n",
    "                print('非法输入!')\n",
    "\n",
    "#这个封装的目的是让用户输入合法的入库商品的数量和金额                \n",
    "def quantity_price():\n",
    "    while True:\n",
    "        q=input('请输入数量: ')\n",
    "        p=input('请输入单价: ')\n",
    "        try:#用了新学的try except语句，我觉得看起来比大量的if简洁。\n",
    "            q=int(q)\n",
    "            p=float(p)#这个句法的含义是，try是否报错，如果不报错就执行下一步           \n",
    "            match (q,p):\n",
    "                case (a,b) if a >=0 and b >=0:\n",
    "                    return (a,b)\n",
    "                case _:\n",
    "                    print('数量和单价必须是非负数!')\n",
    "        except ValueError:\n",
    "            print('输入必须是数字,请重新输入!')\n",
    "\n",
    " #这个数量的目的是为了给出库功能提供合法的出库数量                   \n",
    "def quantity():\n",
    "    while True:\n",
    "        q=input('请输入数量: ')\n",
    "        try:#在try的时候把约束条件都写好，q必须是整数，或者理解为q=int(q)为True，也是转化为布尔值判断。\n",
    "            q=int(q)\n",
    "            match q:\n",
    "                case s if s >0:\n",
    "                    return s\n",
    "                case _:\n",
    "                    print('输入必须是大于零的整数，请重新输入!')\n",
    "        except ValueError:\n",
    "            print('输入必须是大于零的整数，请重新输入!')\n",
    "\n",
    "#在许多情形下需要对品类进行选择操作而设置的固定封装，return n的原因是print只是展示打印，无法对选择的项目进行操作，因此要通过n和序号对应的索引取得品类名称\n",
    "def categorylist():\n",
    "    print('商品品类及指令如下')\n",
    "    print('1.生鲜食品')\n",
    "    print('2.包装食品')\n",
    "    print('3.酒水')\n",
    "    print('4.家电')\n",
    "    print('5.厨具')\n",
    "    print('6.卫生日用品')\n",
    "    print('7.退出程序')\n",
    "    n=['生鲜食品','包装食品','酒水','家电','厨具','卫生日用品','退出程序']\n",
    "    return n\n",
    "\n",
    "KEY_NAME = globals().get('KEY_NAME', '品名')\n",
    "KEY_QTY  = globals().get('KEY_QTY', '数量')\n",
    "KEY_PRICE= globals().get('KEY_PRICE','单价')\n",
    "KEY_VALUE= globals().get('KEY_VALUE','总价')\n",
    "\n",
    "def assert_state_ok():\n",
    "    for cat in commodities:\n",
    "        names1=set(commodities[cat])\n",
    "        names2={d['品名'] for d in storage[cat]}\n",
    "        assert names1==names2, f'品名不同步:{cat}'\n",
    "        for d in storage[cat]:\n",
    "            q,p,v=d[KEY_QTY],d[KEY_PRICE],d[KEY_VALUE]\n",
    "            assert q >=0 and p >=0 and v >=0, f'出现负数:{d}'\n",
    "            if q:\n",
    "                assert abs(v-q*p)<1e-6,f'总价≠数量*单价:{d}'\n",
    "\n",
    "def run_smoke_tests():\n",
    "    #任意选择一个品类并录入商品\n",
    "    cat='家电'\n",
    "    name='电饭锅'\n",
    "    if name not in commodities[cat]:\n",
    "        commodities[cat].append(name)\n",
    "        storage[cat].append({KEY_NAME:name,KEY_QTY:0,KEY_PRICE:0.0,KEY_VALUE:0.0})\n",
    "    #按不同价格入库两次\n",
    "    for q,p in [(3,100),(1,200)]:\n",
    "        item=next((d for d in storage[cat] if d[KEY_NAME]==name),None)\n",
    "        new_q=item[KEY_QTY]+q\n",
    "        new_v=item[KEY_VALUE]+q*p\n",
    "        item[KEY_QTY]=new_q\n",
    "        item[KEY_VALUE]=new_v\n",
    "        item[KEY_PRICE]=new_v/new_q if new_q else 0.0\n",
    "    #出库2，期望数量2，总价250，单价125\n",
    "    item=next((d for d in storage[cat] if d[KEY_NAME]==name),None)\n",
    "    out=2\n",
    "    item[KEY_QTY]-=out\n",
    "    item[KEY_VALUE]-=out*item[KEY_PRICE]\n",
    "    if item[KEY_QTY]==0:\n",
    "        item[KEY_PRICE]=0.0\n",
    "    print('冒烟测试通过!')\n",
    "    assert_state_ok()\n",
    "\n",
    "#state={\n",
    "#    'commodities':commodities,\n",
    "#    'storage':storage,\n",
    "#    'in_storage':in_storage,\n",
    "#    'out_storage':out_storage\n",
    "#}\n",
    "\n",
    "from pathlib import Path\n",
    "import json\n",
    "\n",
    "STATE_PATH=Path.home()/'Desktop'/'project save'/'Inventory.json'#把Path路径对象赋值给STATE_PATH这个变量\n",
    "\n",
    "def snapshot_state():#封装'快照'这个模块，把四个关键字典装入一个大字典，确保写入和读取值一致\n",
    "    return{\n",
    "        'commodities':commodities,\n",
    "        'storage':storage,\n",
    "        'in_storage':in_storage,\n",
    "        'out_storage':out_storage\n",
    "    }\n",
    "\n",
    "def save_state(state,path:Path|str=STATE_PATH):#设置存储模块，形式参数state是可以由json文件识别的待赋值变量(或参数)，存储路径path要么是Path对象，要么是字符串\n",
    "    p=Path(path)#把path用作为Path这个类的对象，目的是通过Path的处理方式(行为)让path成为当前系统可识别的路径\n",
    "    p.parent.mkdir(parents=True,exist_ok=True)#识别并生成path这个路径的各级文件地址(母地址)，假设已存在也不报错\n",
    "    with p.open('w',encoding='utf-8')as f:#写入方式打开，保证中文能正常保存，用f表示文件对象(这里注意和路径对象进行区分)\n",
    "        json.dump(state,f,ensure_ascii=False,indent=2)#把state序列化成json并写入文件对象f，确保中文能直接写入。\n",
    "\n",
    "def load_state(path:Path|str=STATE_PATH):#设置读取模块，读取路径path要么是Path对象，要么是字符串\n",
    "    p=Path(path)#把path作为Path这个类的对象，目的是通过Path这个类的处理方式(行为)让path成为当前系统可识别的路径\n",
    "    if not p.exists():#如果被Path的这个path路径不存在\n",
    "        return snapshot_state()#返回'快照'的结果，如果本身没有事先存储过任何文件，那读取出来的大字典理应没有任何值(除了初始化的值)，如果有存储过文件，但读取路径和存储路径不一致，那读取路径中的大字典也是没有任何值(除了初始化的值)\n",
    "    with p.open('r',encoding='utf-8')as f:#用读取的方式打开文件对象f，保证中文能正常读取\n",
    "        return json.load(f)#返回json调取文件对象f的值，json.load本身是一个函数,表示从文件对象f读取文本并解析成Python数据结构\n",
    "\n",
    "loaded=load_state(STATE_PATH)#把用读取从STATE_PATH读取来的文件对象的Python数据结构的值赋值给变量loaded\n",
    "\n",
    "commodities=loaded.get('commodities',commodities)\n",
    "storage=loaded.get('storage',storage)\n",
    "in_storage=loaded.get('in_storage',in_storage)\n",
    "out_storage=loaded.get('out_storage',out_storage)#四个全局变量commodities，storage，in_storage，out_storage都从loaded中直接赋值，由默认值变为上次存储的值(如有)，如没有就还是默认值(快照值)\n",
    "                      \n",
    "print('欢迎使用商品库存管理系统!\\n是否进入程序？')\n",
    "start=choice_yn(prompt='请输入y/n: ')\n",
    "match start:#开始的时候就用match函数进行选择\n",
    "    case 'n':\n",
    "        print('感谢使用,再见!')\n",
    "    case 'y':\n",
    "        while True:#进入大循环\n",
    "            print('感谢使用商品库存管理系统,请查看功能清单:')\n",
    "            print('1.维护商品清单')\n",
    "            print('2.入库管理')\n",
    "            print('3.出库管理')\n",
    "            print('4.库存总览')\n",
    "            print('5.退出功能')\n",
    "            main_func=func_num(5)#5个功能选项，用封装的选择功能5\n",
    "            match main_func:\n",
    "                case 1:\n",
    "                    print('欢迎使用商品清单维护功能，请选择要执行的操作: ')\n",
    "                    print('1.查看商品清单')\n",
    "                    print('2.维护商品清单')\n",
    "                    print('3.退出本菜单')\n",
    "                    commolist_choice=func_num(3)\n",
    "                    match commolist_choice:\n",
    "                        case 1:\n",
    "                            print('是否查看汇总信息?')\n",
    "                            showall=choice_yn(prompt='请输入y/n: ')\n",
    "                            if showall=='y':\n",
    "                                cate_count=0\n",
    "                                commo_count=0\n",
    "                                for category,names in commodities.items():\n",
    "                                    onlyshow,commos=safe_enu(commodities[category],empty='该品类下无商品')#分别取safe_enu输出的两个有效结果\n",
    "                                    print(f'{category}:\\n{onlyshow}')\n",
    "                                    if names:#直接用最简单语句判断names是否为空\n",
    "                                        cate_count+=1#品类下有商品，则品类有效计数+1\n",
    "                                        commo_count+=len(names)#品类下有商品，则商品计数+该品类下的列表的len--元素个数\n",
    "                                    else:#这里不写也行，可以默认不处理\n",
    "                                        cate_count+=0\n",
    "                                        commo_count+=0\n",
    "                                print(f'目前商品清单中共有{cate_count}个品类，合计{commo_count}种商品')\n",
    "                            else:\n",
    "                                #categorylist()#这里一开始我还单独写了一遍，但发现执行的时候会出现两块品类选择界面，然后才知道把封装结果赋予变量的时候，print的东西也能显示出来\n",
    "                                catelist=categorylist()\n",
    "                                cate_choice=func_num(7)\n",
    "                                match cate_choice:\n",
    "                                    case 1|2|3|4|5|6:\n",
    "                                        onlyshow,commos=safe_enu(commodities[catelist[cate_choice-1]],empty='该品类下无商品')#分别取safe_enu输出的两个有效结果\n",
    "                                        print(f'{catelist[cate_choice-1]}:\\n{onlyshow}')\n",
    "                                    case 7:\n",
    "                                        print('谢谢使用!')\n",
    "                        \n",
    "                        case 2:\n",
    "                            #categorylist()\n",
    "                            catelist=categorylist()\n",
    "                            cate_choice=func_num(7)\n",
    "                            match cate_choice:\n",
    "                                case 1|2|3|4|5|6:\n",
    "                                    commo_name=inputname()                                       \n",
    "                                    if commo_name in commodities[catelist[cate_choice-1]]:\n",
    "                                        print('商品已存在!')\n",
    "                                    else:\n",
    "                                        print(f'你录入的食品名称是{commo_name},品类是{catelist[cate_choice-1]},确认录入吗?y/n')\n",
    "                                        add_choice=choice_yn(prompt='请输入y/n: ')     \n",
    "                                        if add_choice=='y':\n",
    "                                            commodities[catelist[cate_choice-1]].append(commo_name)#同步添加商品清单中的品名和库存商品中的品名\n",
    "                                            storage_dict={}\n",
    "                                            storage_dict.update({\n",
    "                                                '品名':commo_name,#同步添加商品清单中的品名和库存商品中的品名\n",
    "                                                '数量':0,\n",
    "                                                '单价':0,\n",
    "                                                '总价':0#通过商品维护清单间接添加的库存商品默认初始数量、单价、总价都是0\n",
    "                                            })\n",
    "                                            storage[catelist[cate_choice-1]].append(storage_dict)#把新增商品字典添加到storage字典嵌套的列表中\n",
    "                                            save_state(snapshot_state(),STATE_PATH)\n",
    "                                            print('添加成功!')\n",
    "                                        else:\n",
    "                                            print('谢谢使用!')\n",
    "\n",
    "                                case _:\n",
    "                                    print('谢谢使用!')\n",
    "\n",
    "                        case 3:\n",
    "                            print('谢谢使用!')\n",
    "\n",
    "                case 2:\n",
    "                    print('欢迎使用入库管理功能,请选择要执行的操作:')\n",
    "                    print('1.商品入库')\n",
    "                    print('2.退出本级菜单')\n",
    "                    instore_choice=func_num(2)                    \n",
    "                    match instore_choice:\n",
    "                        case 1:\n",
    "                            #categorylist()\n",
    "                            catelist=categorylist()\n",
    "                            cate_choice=func_num(7)\n",
    "                            match cate_choice:\n",
    "                                case 1|2|3|4|5|6:\n",
    "                                    if len(commodities[catelist[cate_choice-1]])==0:#某品类下无商品，则添加商品并入库，两个动作同时做\n",
    "                                        print('是否添加商品并入库?')\n",
    "                                        choice=choice_yn(prompt='请输入y/n: ')                                        \n",
    "                                        if choice=='y':\n",
    "                                            storage_dict={}\n",
    "                                            commo_name=inputname()\n",
    "                                            q,p=quantity_price()\n",
    "                                            v=float(q*p)\n",
    "                                            commodities[catelist[cate_choice-1]].append(commo_name)\n",
    "                                            storage_dict.update({\n",
    "                                                '品名':commo_name,\n",
    "                                                '数量':q,\n",
    "                                                '单价':p,\n",
    "                                                '总价':v\n",
    "                                            })\n",
    "                                            storage[catelist[cate_choice-1]].append(storage_dict)#把新增商品字典添加到storage字典嵌套的列表中\n",
    "                                            save_state(snapshot_state(),STATE_PATH)\n",
    "                                            print(f'你添加并入库的商品类别为{catelist[cate_choice-1]}\\n品名:{commo_name}\\n数量:{q},单价:{p}\\n总价:{v}')\n",
    "                                        else:\n",
    "                                            print('谢谢使用!')\n",
    "                                    else:#某品类下有商品\n",
    "                                        onlyshow,commos=safe_enu(commodities[catelist[cate_choice-1]],empty='该品类下无商品')\n",
    "                                        print(f'{catelist[cate_choice-1]}:\\n{onlyshow}\\n{len(commos)+1}.添加清单外商品\\n{len(commos)+2}.退出程序')#尝试在safe_enu的显示清单下增加添加清单外商品和退出程序这两个选项\n",
    "                                        choice=func_num(len(commos)+2)\n",
    "                                        match choice:\n",
    "                                            case c if c <=len(commos):#所选的商品在已有商品之内\n",
    "                                                commo_name=commos[choice]#commos是一个字典，不是列表，choice是key的值，name是value\n",
    "                                                q,p=quantity_price()\n",
    "                                                v=float(q*p)\n",
    "                                                #current_q=storage[catelist[cate_choice-1]]['数量']\n",
    "                                                #current_p=storage[catelist[cate_choice-1]]['单价']\n",
    "                                                #current_v=storage[catelist[cate_choice-1]]['总价']\n",
    "                                                for exist_commos in storage[catelist[cate_choice-1]]:\n",
    "                                                    #current_q=exist_commos['数量']\n",
    "                                                    #current_p=exist_commos['单价']\n",
    "                                                    #current_v=exist_commos['总价']\n",
    "                                                    #if commo_name in exist_commos['品名']:\n",
    "                                                    if commo_name == exist_commos['品名']:#完全相等最准确，避免出现包含相同字符的情形\n",
    "                                                    \n",
    "                                                        current_q=exist_commos['数量']\n",
    "                                                        current_p=exist_commos['单价']\n",
    "                                                        current_v=exist_commos['总价']#当选择的序号对应的商品名在遍历过程中被找到才提取其原数量、单价、总价。如过提前提取，后面计算时可能会不知道使用哪一个产品的信息\n",
    "                                                                                                                \n",
    "                                                        print('是否确认入库?')\n",
    "                                                        in_choice=choice_yn(prompt='请输入y/n: ')\n",
    "                                                        match in_choice:\n",
    "                                                            case 'y':\n",
    "                                                                #current_q=current_q+q\n",
    "                                                                #current_v=current_v+v\n",
    "                                                                #current_p=current_v/current_q\n",
    "                                                                new_q=current_q+q\n",
    "                                                                new_v=current_v+v\n",
    "                                                                new_p=new_v/new_q if new_q else 0.0\n",
    "                                                                exist_commos['数量']=new_q\n",
    "                                                                exist_commos['总价']=new_v\n",
    "                                                                exist_commos['单价']=new_p#这里一开始做错了，应该用原字典的键值修改方式把value进行更改，否则修改的只是变量而非字典本身\n",
    "                                                                save_state(snapshot_state(),STATE_PATH)\n",
    "                                                                print(f'入库成功!库存商品信息如下: \\n品类:{catelist[cate_choice-1]}\\n品名:{commo_name}\\n数量:{new_q}\\n单价:{new_p}\\n总价:{new_v}')\n",
    "                                                                break\n",
    "                                                            case _:\n",
    "                                                                print('谢谢使用!')\n",
    "\n",
    "\n",
    "                                            case x if x ==len(commos)+1:\n",
    "                                                commo_name=inputname()\n",
    "                                                \n",
    "                                                #if commo_name in commos:\n",
    "                                                \n",
    "                                                if commo_name in commodities[catelist[cate_choice-1]]:\n",
    "                                                    print('商品已存在!请返回选择商品编号添加入库')\n",
    "                                                else:\n",
    "                                                    q,p=quantity_price()\n",
    "                                                    v=float(q*p)\n",
    "                                                    print('是否确认入库?')\n",
    "                                                    in_choice=choice_yn(prompt='请输入y/n: ')\n",
    "                                                    match in_choice:\n",
    "                                                        case 'y':\n",
    "                                                            storage_dict={}\n",
    "                                                            commodities[catelist[cate_choice-1]].append(commo_name)\n",
    "                                                            storage_dict.update({\n",
    "                                                                '品名':commo_name,\n",
    "                                                                '数量':q,\n",
    "                                                                '单价':p,\n",
    "                                                                '总价':v\n",
    "                                                            })\n",
    "                                                            storage[catelist[cate_choice-1]].append(storage_dict)\n",
    "                                                            save_state(snapshot_state(),STATE_PATH)\n",
    "                                                            print(f'你添加并入库的商品类别为{catelist[cate_choice-1]}\\n品名:{commo_name}\\n数量:{q},单价:{p}\\n总价:{v}')\n",
    "                                                            #要补一个本身已经存在的in_storage字典信息\n",
    "                                                        case _:\n",
    "                                                            print('谢谢使用!')\n",
    "\n",
    "                                            case x if x ==len(commos)+2:\n",
    "                                                print('谢谢使用!')\n",
    "\n",
    "                        case 2:\n",
    "                            print('谢谢使用!')\n",
    "\n",
    "                case 3:\n",
    "                    print('欢迎使用出库管理功能，请选择要出库的品类:')\n",
    "                    #categorylist()\n",
    "                    catelist=categorylist()\n",
    "                    cate_choice=func_num(7)\n",
    "                    match cate_choice:\n",
    "                        case 1|2|3|4|5|6:\n",
    "                            if len(commodities[catelist[cate_choice-1]])==0:\n",
    "                                print('该品类下没有可出库商品,请先添加品类或入库!')\n",
    "                            else:\n",
    "                                print('请选择要出库的商品或指令: ')\n",
    "                                onlyshow,commos=safe_enu(commodities[catelist[cate_choice-1]],empty='该品类下无商品')\n",
    "                                print(f'{catelist[cate_choice-1]}:\\n{onlyshow}\\n{len(commos)+1}.退出本菜单')\n",
    "                                choice=func_num(len(commos)+1)\n",
    "                                match choice:\n",
    "                                    case c if c <=len(commos):\n",
    "                                        commo_name=commos[choice]\n",
    "                                        for exist_commos in storage[catelist[cate_choice-1]]:\n",
    "                                            #current_q=exist_commos['数量']\n",
    "                                            #current_p=exist_commos['单价']\n",
    "                                            #current_v=exist_commos['总价']\n",
    "                                            if commo_name in exist_commos['品名']:                                           \n",
    "                                                current_q=exist_commos['数量']\n",
    "                                                current_p=exist_commos['单价']\n",
    "                                                current_v=exist_commos['总价']\n",
    "                                                print(f'现有库存数量为{current_q},请输入要出库的数量')\n",
    "                                                q=quantity()\n",
    "                                                if q <= current_q:\n",
    "                                                    print(f'确定按数量{q}出库此商品吗?')\n",
    "                                                    out_choice=choice_yn(prompt='请输入y/n: ')\n",
    "                                                    match out_choice:\n",
    "                                                        case 'y':\n",
    "                                                            #current_q=current_q-q\n",
    "                                                            #current_v=current_v-q*current_p\n",
    "                                                            new_q=current_q-q\n",
    "                                                            new_v=current_v-q*current_p\n",
    "                                                            exist_commos['数量']=new_q\n",
    "                                                            exist_commos['总价']=new_v\n",
    "                                                            save_state(snapshot_state(),STATE_PATH)\n",
    "                                                            print(f'出库成功!\\n本次出库{q}件商品\\n库存商品信息如下: \\n品类:{catelist[cate_choice-1]}\\n品名:{commo_name}\\n数量:{new_q}\\n单价:{current_p}\\n总价:{new_v}')                                                            \n",
    "                                                        case _:\n",
    "                                                            print('谢谢使用!')\n",
    "                                                else:\n",
    "                                                    print('出库数不能大于库存数!')\n",
    "\n",
    "                                    case c if c==len(commos)+1:\n",
    "                                        print('谢谢使用!')\n",
    "                        case 7:\n",
    "                            print('谢谢使用!')\n",
    "\n",
    "                case 4:\n",
    "                    print('欢迎进入库存总览功能,请选择指令: ')\n",
    "                    print('1.分类查看')\n",
    "                    print('2.汇总查看')\n",
    "                    print('3.退出程序')\n",
    "                    choice=func_num(3)\n",
    "                    match choice:\n",
    "                        case 1:\n",
    "                            #categorylist()\n",
    "                            catelist=categorylist()\n",
    "                            cate_choice=func_num(7)\n",
    "                            match cate_choice:#第一次忘记了要选品类了\n",
    "                                case 1|2|3|4|5|6:\n",
    "                                    print(f'你选的品类是{catelist[cate_choice-1]},请选择要执行的指令: ')\n",
    "                                    print('1.查看汇总信息')\n",
    "                                    print('2.查看明细信息')\n",
    "                                    print('3.退出程序')\n",
    "                                    op_choice=func_num(3)\n",
    "                                    match op_choice:\n",
    "                                        case 1:\n",
    "                                            if len(commodities[catelist[cate_choice-1]])==0:\n",
    "                                                print('该品类下没有可查看的商品,请先添加品类或入库!')\n",
    "                                            else:\n",
    "                                                sum_q=0\n",
    "                                                avg_p=0\n",
    "                                                sum_v=0\n",
    "                                                for exist_commos in storage[catelist[cate_choice-1]]:\n",
    "                                                    current_q=exist_commos['数量']\n",
    "                                                    current_p=exist_commos['单价']\n",
    "                                                    current_v=exist_commos['总价']\n",
    "                                                    sum_q=sum_q+current_q\n",
    "                                                    sum_v=sum_v+current_v                                                    \n",
    "                                                avg_p=sum_v/sum_q if sum_q else 0.0\n",
    "                                                print(f'品类:{catelist[cate_choice-1]}\\n商品品种数:{len(commodities[catelist[cate_choice-1]])}\\n商品总数量:{sum_q}\\n平均单价:{avg_p}\\n总价值:{sum_v}')\n",
    "\n",
    "                                        case 2:\n",
    "                                            if len(commodities[catelist[cate_choice-1]])==0:\n",
    "                                                print('该品类下没有可查看的商品,请先添加品类或入库!')\n",
    "                                            else:\n",
    "                                                print('请查看商品信息: ')\n",
    "                                                for exist_commos in storage[catelist[cate_choice-1]]:\n",
    "                                                    print(exist_commos)\n",
    "                                        case 3:\n",
    "                                            print('谢谢使用!')\n",
    "                                case 7:\n",
    "                                    print('谢谢使用!')\n",
    "                        \n",
    "                        case 2:\n",
    "                            all_commos=0\n",
    "                            all_q=0\n",
    "                            all_avg_p=0\n",
    "                            all_v=0\n",
    "                            for category,sumcommos in storage.items():\n",
    "                                if sumcommos:\n",
    "                                    sum_q=0\n",
    "                                    ave_p=0\n",
    "                                    sum_v=0\n",
    "                                    for exist_commos in sumcommos:\n",
    "                                        current_q=exist_commos['数量']\n",
    "                                        current_p=exist_commos['单价']\n",
    "                                        current_v=exist_commos['总价']\n",
    "                                        sum_q=sum_q+current_q\n",
    "                                        sum_v=sum_v+current_v\n",
    "                                        #all_commos=all_commos+len(sumcommos)\n",
    "                                        #all_q=all_q+sum_q\n",
    "                                        #all_v=all_v+sum_v\n",
    "                                    avg_p=sum_v/sum_q if sum_q else 0.0\n",
    "                                    print(f'品类:{category}\\n商品品种数:{len(sumcommos)}\\n商品总数量:{sum_q}\\n平均单价:{avg_p}\\n总价值:{sum_v}')\n",
    "                                    #所有商品汇总的计算要放在商品品种遍历之外，因为sum_q在每次遍历都会累加，而all_q只要取sum_q的最后结果即可，如果放在商品品种遍历里面会导致把sum_q累加的过程数也加进去了，结果错误\n",
    "                                    all_commos=all_commos+len(sumcommos)\n",
    "                                    all_q=all_q+sum_q#\n",
    "                                    all_v=all_v+sum_v\n",
    "                                else:\n",
    "                                    print(f'品类:{category}下无在库商品')\n",
    "                            all_avg_p=all_v/all_q if all_q else 0.0\n",
    "                            print(f'库内商品总品种数:{all_commos}\\n总数量:{all_q}\\n平均单价:{all_avg_p}\\n总价值:{all_v}')\n",
    "                                #all_commos=all_commos+len(sumcommos)\n",
    "                                #all_q=all_q+sum_q\n",
    "                                #all_v=all_v+sum_v\n",
    "                            #all_avg_p=all_v/all_q if all_q else 0.0\n",
    "                            #print(f'库内商品总品种数:{all_commos}\\n总数量:{all_q}\\n平均单价:{all_avg_p}\\n总价值:{all_v}')\n",
    "\n",
    "                        case 3:\n",
    "                            print('谢谢使用!')\n",
    "\n",
    "                case 5:\n",
    "                    print('谢谢使用!再见！')\n",
    "                    break"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "80f1a814-c751-4e32-838a-3759c7c2bf76",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python [conda env:base]",
   "language": "python",
   "name": "conda-base-py"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
